package hardware;


@SuppressWarnings("rawtypes")
public class Instruction implements Comparable{
	final static String LOAD_WORD = "LW";
	final static String STORE_WORD = "SW";
	final static String JUMP = "JMP";
	final static String BRANCH_IF_EQUAL = "BEQ";
	final static String JUMP_AND_LINK = "JALR";
	final static String RETURN = "RET"; // Unconditional jump
	final static String ADD = "ADD";
	final static String ADDI = "ADDI";
	final static String NAND = "NAND";
	final static String MULT = "MUL";
	final static String DIV = "DIV";
	final static String UNDEFINED = "undifined";
	final static String STATE_FETCH = "fetch";
	final static String STATE_DECODE = "decode";
	final static String STATE_DISPATCH = "dispatch";
	final static String STATE_ISSUE = "issue";
	final static String STATE_EXEC = "execute";
	final static String STATE_COMMIT = "commit";

	private String type, currentState;
	private Register regA, regB, regC, originalLogical, originalRegB, oriRegC;
	private long fetchStartTime, fetchEndTime, decStartTime, decEndTime, dispStartTime, dispEndTime,
			issueStartTime, issueEndTime, execStartTime, execEndTime,
			commitStartTime, commitEndTime, cacheStartTime, cacheEndTime;
	private int immediate;
	private boolean commited;


	public long getIssueStartTime() {
		return issueStartTime;
	}

	public void setIssueStartTime(long issueStartTime) {
		this.issueStartTime = issueStartTime;
	}

	public long getIssueEndTime() {
		return issueEndTime;
	}

	public void setIssueEndTime(long issueEndTime) {
		this.issueEndTime = issueEndTime;
	}

	private int ID, executionTime;

	public Instruction() {
		this.setType(UNDEFINED);
		this.setRegA(new Register());
		this.regB = new Register();
		this.regC = new Register();
		this.immediate = 0;
	}

	public Instruction(int Id,String type, Register regA, Register regB,
			Register regC, int immediate, int ex) {
		this.setType(type);
		this.setRegA(regA);
		this.originalLogical = regA;
		this.originalRegB = regB;
		this.oriRegC = regC;
		this.regB = regB;
		this.regC = regC;
		this.immediate = immediate;
		this.setExecutionTime(ex);
		this.ID = Id;
	}

	public Register getRegB() {
		return regB;
	}

	public void setRegB(Register regB) {
		this.regB = regB;
	}

	public Register getRegC() {
		return regC;
	}

	public void setRegC(Register regC) {
		this.regC = regC;
	}

	public int getImmediate() {
		return immediate;
	}

	public void setImmediate(int immediate) {
		this.immediate = immediate;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public Register getRegA() {
		return regA;
	}

	public void setRegA(Register regA) {
		this.regA = regA;
	}

	public String getCurrentState() {
		return currentState;
	}

	public void setCurrentState(String currentState) {
		this.currentState = currentState;
	}

	public long getDecStartTime() {
		return decStartTime;
	}

	public void setDecStartTime(long decStartTime) {
		this.decStartTime = decStartTime;
	}

	public long getDecEndTime() {
		return decEndTime;
	}

	public void setDecEndTime(long decEndTime) {
		this.decEndTime = decEndTime;
	}

	public long getDispStartTime() {
		return dispStartTime;
	}

	public void setDispStartTime(long dispStartTime) {
		this.dispStartTime = dispStartTime;
	}

	public long getDispEndTime() {
		return dispEndTime;
	}

	public void setDispEndTime(long dispEndTime) {
		this.dispEndTime = dispEndTime;
	}

	public long getExecStartTime() {
		return execStartTime;
	}

	public void setExecStartTime(long execStartTime) {
		this.execStartTime = execStartTime;
	}

	public long getExecEndTime() {
		return execEndTime;
	}

	public void setExecEndTime(long execEndTime) {
		this.execEndTime = execEndTime;
	}

	public long getCommitStartTime() {
		return commitStartTime;
	}

	public void setCommitStartTime(long commitStartTime) {
		this.commitStartTime = commitStartTime;
	}

	public long getCommitEndTime() {
		return commitEndTime;
	}

	public void setCommitEndTime(long commitEndTime) {
		this.commitEndTime = commitEndTime;
	}

	public int getID() {
		return ID;
	}

	public void setID(int iD) {
		ID = iD;
	}

	public int getExecutionTime() {
		return executionTime;
	}

	public void setExecutionTime(int executionTime) {
		this.executionTime = executionTime;
	}

	@Override
	public boolean equals(Object o) {
		Instruction r = (Instruction) o;
		if (this.ID == r.ID) {
			return true;
		}
		return false;
	}

	public String toString(){
		String ins;
		ins = "name: "+ ID + "fetch: " + fetchStartTime + " - " + fetchEndTime +  " decode: " + decStartTime + " - " + decEndTime + "dispatch: " + dispStartTime + " - " + dispEndTime + "issue: " + issueStartTime + " - " + issueEndTime
				+ "execute: " + execStartTime + " - " + execEndTime + "CommitTime: " + commitEndTime;
		
		return ins;
	}
	
	public String shortToString(){
		String ins;
		if(oriRegC != null){
		ins = "Name: "+ ID  + " " + type + ", " + originalLogical.getName() + ", " + originalRegB.getName() + ", " + oriRegC.getName() + ", " + immediate;
		}else
			ins = "Name: "+ ID  + " " + type + ", " + originalLogical.getName() + ", " + originalRegB.getName() + ", " + immediate;
		return ins;
	}

	public long getCacheStartTime() {
		return cacheStartTime;
	}

	public void setCacheStartTime(long cacheStartTime) {
		this.cacheStartTime = cacheStartTime;
	}

	public long getCacheEndTime() {
		return cacheEndTime;
	}

	public void setCacheEndTime(long cacheEndTime) {
		this.cacheEndTime = cacheEndTime;
	}

	public long getFetchStartTime() {
		return fetchStartTime;
	}

	public void setFetchStartTime(long fetchStartTime) {
		this.fetchStartTime = fetchStartTime;
	}

	public long getFetchEndTime() {
		return fetchEndTime;
	}

	public void setFetchEndTime(long fetchEndTime) {
		this.fetchEndTime = fetchEndTime;
	}

	public boolean isCommited() {
		return commited;
	}

	public void setCommited(boolean commited) {
		this.commited = commited;
	}

	public Register getOriginalLogical() {
		return originalLogical;
	}

	public void setOriginalLogical(Register originalLogical) {
		this.originalLogical = originalLogical;
	}

	public Register getOriginalRegB() {
		return originalRegB;
	}

	public void setOriginalRegB(Register originalRegB) {
		this.originalRegB = originalRegB;
	}

	public Register getOriRegC() {
		return oriRegC;
	}

	public void setOriRegC(Register oriRegC) {
		this.oriRegC = oriRegC;
	}

	@Override
	public int compareTo(Object o) {
		Instruction i = (Instruction) o;
		if (this.getCommitEndTime() > i.getCommitEndTime()) {
			return 1;
		} else if (this.getCommitEndTime() < i.getCommitEndTime()) {
			return -1;
		} else {
			if (this.getID() < i.getID()) {
				return -1;
			} else {
				return 1;
			}
		}		
	}
}
